package goutl

import (
	"encoding/csv"
	"gitee.com/xiakai001/goutl/errutl"
	"github.com/antlabs/pcurl"
	"io"
	"net/http"
	"strings"
)

// Curl 执行curl
func Curl(curl string) (string, error) {

	req, err := NewHttpReqByCurl(curl)
	if err != nil {
		return "", errutl.Wrap(err)
	}

	resp, err := req.Ask()
	if err != nil {
		return "", errutl.Wrap(err)
	}

	return resp, nil
}

type HttpReq struct {
	Request *http.Request
}

func (h *HttpReq) Do() (*http.Response, error) {

	if h == nil || h.Request == nil {
		return nil, errutl.New("req is nil!")
	}

	resp, err := http.DefaultClient.Do(h.Request)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return resp, nil
}

func (h *HttpReq) Ask() (string, error) {

	resp, err := h.Do()
	if err != nil {
		return "", errutl.Wrap(err)
	}

	defer func() { _ = resp.Body.Close() }()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", errutl.Wrap(err)
	}

	return string(body), nil
}

// NewHttpReq 创建http req
func NewHttpReq(method, url string, body interface{}) (*HttpReq, error) {

	var bodyStr string
	bodyStr, ok := body.(string)
	if !ok {
		bodyStr = Json(body)
	}

	request, err := http.NewRequest(method, url, strings.NewReader(bodyStr))
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return &HttpReq{Request: request}, nil
}

// NewHttpReqByCurl 根据curl创建http req, 必须有-X参数
func NewHttpReqByCurl(curl string) (*HttpReq, error) {
	curl = strings.ReplaceAll(curl, "\\", " ")
	curl = strings.ReplaceAll(curl, "\n", " ")

	req, err := pcurl.ParseAndRequest(curl)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	if req.Body != http.NoBody && req.Method == http.MethodGet {
		req.Method = http.MethodPost
	}

	return &HttpReq{req}, nil
}

func ReadTopLineFromHttp[V any](url string, top int, skip func(string) bool, deal func(string) V, fn func(index int, line V)) error {
	return ReadTopLineFromHttpWithErr(url, top, skip, deal, func(index int, line V) error {
		fn(index, line)
		return nil
	})
}

func ReadTopLineFromHttpWithErr[V any](url string, top int, skip func(string) bool, deal func(string) V, fn func(index int, line V) error) error {

	body, err := getHttpBody(url)
	if err != nil {
		return errutl.Wrap(err)
	}

	err = readLinesFromReader(body, top, skip, deal, fn)
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

// ReadLineFromHttp 按行遍历http返回结果
func ReadLineFromHttp[V any](url string, skip func(string) bool, deal func(string) V, fn func(index int, line V)) error {
	return ReadTopLineFromHttp(url, 0, skip, deal, fn)
}

// GetStringFromHttp 获取http返回结果
func GetStringFromHttp(url string) (string, error) {

	body, err := getHttpBody(url)
	if err != nil {
		return "", errutl.Wrap(err)
	}

	defer func() { _ = body.Close() }()
	str, err := getStringFromReader(body)
	if err != nil {
		return "", errutl.Wrap(err)
	}

	return str, nil
}

// GetCsvFromHttp 获取csv格式文件内容
func GetCsvFromHttp(url string) ([][]string, error) {
	body, err := getHttpBody(url)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	defer func() { _ = body.Close() }()

	csvReader := csv.NewReader(body)
	matrix, err := csvReader.ReadAll()
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return matrix, nil
}

// DownloadFromHttp 下载http数据
func DownloadFromHttp(url string, filePath string, bufferSize int) error {

	body, err := getHttpBody(url)
	if err != nil {
		return errutl.Wrap(err)
	}

	defer func() { _ = body.Close() }()

	writer, err := NewFileWriter(filePath, false)
	if err != nil {
		return errutl.Wrap(err)
	}
	defer func() {
		err := writer.Close()
		errutl.CheckErr(err)
	}()

	if bufferSize <= 0 {
		_, err = io.Copy(writer.file, body)
	} else {
		bufferArr := make([]byte, bufferSize)
		_, err = io.CopyBuffer(writer.file, body, bufferArr)
	}
	if err != nil {
		return errutl.Wrap(err)
	}

	return nil
}

func NewScannerFromHttp(url string) (*Scanner, error) {
	body, err := getHttpBody(url)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	return NewScanner(body), nil
}

func getHttpBody(url string) (io.ReadCloser, error) {

	resp, err := http.Get(url)
	if err != nil {
		return nil, errutl.Wrap(err)
	}

	if resp == nil {
		return nil, errutl.New("resp is nil")
	}

	body := resp.Body
	if body == nil {
		return nil, errutl.New("body is nil")
	}

	return body, nil
}
